﻿namespace PugLib.Benchmarks
{
    using System;
    using System.Diagnostics;
    using System.Reflection;
    using System.Runtime.CompilerServices;

    public static class DelegatePerfComparison
    {
        public static void Run()
        {
            const int limit = 1000000;
            Bar bar = new Bar();
            MethodInfo method = typeof(Bar).GetMethod("Foo");
            Debug.Assert(method != null);
            RunTest(method, 1, bar); //warm up
            Console.WriteLine("--------real test ---------");
            RunTest(method, limit, bar); //warm up
        }

        private static void RunTest(MethodInfo method, int limit, Bar bar)
        {
            Action<int> action;
            Stopwatch stopwatch = Stopwatch.StartNew();
            for (int i = 0; i < limit; i++)
            {
                bar.Foo(i);
            }
            stopwatch.Stop();
            Console.WriteLine("direct call: {0}", stopwatch.ElapsedMilliseconds);
            stopwatch = Stopwatch.StartNew();
            for (int i = 0; i < limit; i++)
            {
                action = new Action<int>(bar.Foo);
                action(i);
            }
            stopwatch.Stop();
            Console.WriteLine("delegate.ctor: {0}", stopwatch.ElapsedMilliseconds);

            stopwatch = Stopwatch.StartNew();
            for (int i = 0; i < limit; i++)
            {
                action = Delegate.CreateDelegate(typeof(Action<int>), bar, method) as Action<int>;
                action(i);
            }
            stopwatch.Stop();
            Console.WriteLine("CreateDelegate closed: {0}", stopwatch.ElapsedMilliseconds);
            stopwatch = Stopwatch.StartNew();
            Action<Bar, int> action2 = Delegate.CreateDelegate(typeof(Action<Bar, int>), method) as Action<Bar, int>;
            for (int i = 0; i < limit; i++)
            {
                action2(bar, i);
            }
            stopwatch.Stop();
            Console.WriteLine("CreateDelegate open: {0}", stopwatch.ElapsedMilliseconds);
        }
    }

    public sealed class Bar
    {
        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
        public void Foo(int param)
        {
        }
    }
}